#include <iostream>
#include <cstdlib>
#include <string>
#include <map>
#include <stack>
#include <vector>

#include "static.hpp"
#include "../ParserLexer/treestruct.hpp"


using namespace std;



/* ---------------------------------------------- */
/*  Transforming à simple C tree from the parser  */
/* into an enhanced C++ tree for the static check */
/* ---------------------------------------------- */

/*
tree_cpp* preprocess_tree(tree simple)
{
    switch (simple.nodetype)
    {
        case variable_node:
        {
            tree_cpp* new_cpp = (tree_cpp*) malloc(sizeof(tree_cpp));
            new_cpp->nodetype = variable_node;

		string * new_string = new string(simple.a_variable);
            new_cpp->a_variable = new_string;
            return new_cpp;
        }
        break;
        case number_node:
        {
            tree_cpp* new_cpp = (tree_cpp*) malloc(sizeof(tree_cpp));;
            new_cpp->nodetype = number_node;
            new_cpp->a_number = simple.a_number;
            return new_cpp;
        }
        break;
        case boolean_node:
        {
            tree_cpp* new_cpp = (tree_cpp*) malloc(sizeof(tree_cpp));
            new_cpp->nodetype = boolean_node;
            new_cpp->a_boolean = simple.a_boolean;
            return new_cpp;
        }
        break;
        case operateur_node:
        {
            tree_cpp* new_cpp = (tree_cpp*) malloc(sizeof(tree_cpp));
            new_cpp->nodetype = operateur_node;
            new_cpp->an_operateur.operateur = simple.an_operateur.operateur;
            if (simple.an_operateur.first)
            {
                new_cpp->an_operateur.first = preprocess_tree(*simple.an_operateur.first);
            }
            if (simple.an_operateur.second)
            {
                new_cpp->an_operateur.second = preprocess_tree(*simple.an_operateur.second);
            }
            if (simple.an_operateur.third)
            {
                new_cpp->an_operateur.third = preprocess_tree(*simple.an_operateur.third);
            }
            return new_cpp;
        }
    }
}
*/


/* --------------------------------- */
/* Checking if a variable is defined */
/*   and/or has a defined content    */
/* --------------------------------- */

int check_variable(string unchecked_var, int argmt)
{
    unsigned int i, res;
    if (argmt == 0)
    {
        res = 0;
        for (i=0;i<var_list.size();i++)
        {
            if (var_list[i].count(unchecked_var) == 1)
            {
                res = 1;
                break;
            }
        }
        if (res == 0)
        {
            access_type* to_insert = new access_type;
            to_insert->access_state = 1;
            to_insert->type = "pending";
            var_list[var_list.size()-1].insert(pair<string,access_type> (unchecked_var,*to_insert));
        }
    }
    else if (argmt == 1)
    {
        res = 0;
        for (i=0;i<var_list.size();i++)
        {
            if (var_list[i].count(unchecked_var))
            {
                res = 1;
                var_list[i][unchecked_var].access_state = 2;
                break;
            }
        }
    }
    else
    {
        res = 0;
        for (i=0;i<var_list.size();i++)
        {
            if (var_list[i].count(unchecked_var))
            {
                if (var_list[i][unchecked_var].access_state == 2)
                {
                    res = 2;
                }
                else
                {
                    res = 1;
                }
                break;
            }
        }
    }
    return res;
}



/* ------------------------------------------------- */
/* Adding a record/array type to a list of variables */
/* ------------------------------------------------- */

void type_add(tree id_tree, char* type_name, enum types type)
{
    if (type == record)
    {
        switch (id_tree.nodetype)
        {
            case variable_node:
            {
                record_list[string((const char*) id_tree.a_variable)] = string((const char*) type_name);
            }
            break;
            case operateur_node:
            {
                record_list[string((const char*) id_tree.an_operateur.first->a_variable)] = string((const char*) type_name);
                if (id_tree.an_operateur.second != NULL)
                {
                    type_add(*id_tree.an_operateur.second, type_name, type);
                }
            }
            break;
        }
    }
    else if (type == array)
    {
        switch (id_tree.nodetype)
        {
            case variable_node:
            {
                array_list[string((const char*) id_tree.a_variable)] = string((const char*) type_name);
            }
            break;
            case operateur_node:
            {
                array_list[string((const char*) id_tree.an_operateur.first->a_variable)] = string((const char*) type_name);
                if (id_tree.an_operateur.second != NULL)
                {
                    type_add(*id_tree.an_operateur.second, type_name, type);
                }
            }
        }
    }
}


/* --------------------------------------------------- */
/* Assigning a type to a variable or list of variables */
/* --------------------------------------------------- */

void assign_type(tree id_tree, char* type_name)
{
    access_type new_var;
    new_var.access_state = 0;
    new_var.type = string((const char*) type_name);

    switch (id_tree.nodetype)
    {
        case variable_node:
        {
            var_list[var_list.size()-1][string((const char*) id_tree.a_variable)] = new_var;
        }
        break;
        case operateur_node:
        {
            var_list[var_list.size()-1][string((const char*) id_tree.an_operateur.first->a_variable)] = new_var;
            assign_type(*id_tree.an_operateur.second, type_name);
        }
        break;
    }
}




/* --------------------------------------------------------- */
/* Assigning a type to a list of fields of a new record type */
/* --------------------------------------------------------- */

void assign_field_type(tree id_tree, char* type_name)
{
    if (type_list.count(string(type_name)) > 0)
    {
        if (id_tree.an_operateur.first->a_variable == NULL)
        {
            printf("oups\n");
        }
        map<string, string> field_type_list;
        if (record_prototype_list.count(curr_defining.top()) > 0)
        {
            field_type_list = record_prototype_list[curr_defining.top()];
        }
        field_type_list[string(id_tree.an_operateur.first->a_variable)] = string(type_name);
        record_prototype_list[curr_defining.top()] = field_type_list;
    }
    else
    {
        cerr << "ERROR : Type used for field \"" << *id_tree.an_operateur.first->a_variable << "\" in record-type \"" << curr_defining.top() << "\" is unknown\n";
        map<string, string> field_type_list;
        if (record_prototype_list.count(curr_defining.top()) > 0)
        {
            field_type_list = record_prototype_list[curr_defining.top()];
            field_type_list[string(id_tree.an_operateur.first->a_variable)] = string(type_name);
        }
        else
        {
            field_type_list[string(id_tree.an_operateur.first->a_variable)] = "pending";
        }
        record_prototype_list[curr_defining.top()] = field_type_list;}
    if (id_tree.an_operateur.second != NULL)
    {
        assign_field_type(*id_tree.an_operateur.second, type_name);
    }
}



/* ---------------------------------------------- */
/* Retrieving the type of a variable (if defined) */
/* ---------------------------------------------- */

char* retrieve_var_type(char* var)
{
    string res;
    unsigned int i;
    bool to_define = true;
    res = "pending";
    for (i=0;i<var_list.size();i++)
    {
        if (var_list[i].count(var))
        {
            to_define = false;
            return (char*) var_list[i][var].type.c_str();
        }
    }
    if (to_define)
    {
        var_list[var_list.size()-1][var].access_state = 1;
        var_list[var_list.size()-1][var].type = "pending";
        return (char*) res.c_str();
    }
    return (char*) res.c_str();
}



/* ------------------------------------------------------ */
/* Auxilary function for the defining of new record types */
/* ------------------------------------------------------ */

map<string, string> convert_to_field(map<string, access_type> old_list)
{
    unsigned int i;
    map<string, string> new_list;
    map<string, access_type>::iterator it = old_list.begin();
    for (i=0;i<old_list.size();i++);
    {
        new_list[it->first] = (it->second).type;
        it++;
    }
    return new_list;
}



/* ---------------------------------------------------- */
/* The main function for the static analysis of the AST */
/* ---------------------------------------------------- */
/* ---------------------------------------------------- */
/*                                                      */
/* This function performs the type check on the         */
/* operands of any integer or boolean operator          */
/* It also checks if variables have been defined when   */
/* they are called and gives an error when a variable   */
/* is used within in an operator without having its     */
/* defined beforehand                                   */
/* It's output is an "decorated" Abstract Syntax Tree   */
/* on which every node has a type, "misc" being the     */
/* default value for nodes where type is not important  */
/*                                                      */
/* ---------------------------------------------------- */

tree_typed* static_analysis(tree unchecked, int var_arg)
{
    string nbool = "boolean";
    string nint = "integer";
    string nrecord = "record";
    string narray = "array";
    string npending = "pending";
    string nmisc = "misc";

    tree_typed* typed = new tree_typed;
    switch(unchecked.nodetype)
    {
        case number_node:
        {
            typed->nodetype = number_node;
            typed->contenttype = (char*) nint.c_str();
            typed->a_number = unchecked.a_number;
            return typed;
        }
        break;
        case boolean_node:
        {

            typed->nodetype = boolean_node;
            typed->contenttype = (char*) nbool.c_str();
            typed->a_boolean = unchecked.a_boolean;
            return typed;
        }
        break;
        case variable_node:
        {
            typed->nodetype = variable_node;
            typed->a_variable = unchecked.a_variable;
            int test = check_variable(unchecked.a_variable, var_arg);
            if (var_arg == 0)
            {
                typed->contenttype = (char*) npending.c_str();
                if (test)
                {
                    cerr << "ERROR : Variable name \"" << unchecked.a_variable << "\" is already in use.\n";
                }
            }
            else if (var_arg == 1)
            {
                typed->contenttype = retrieve_var_type(unchecked.a_variable);
                if (!test)
                {
                    cerr << "ERROR : Variable name \"" << unchecked.a_variable << "\" has not yet been defined\n";
                }
            }
            else
            {
                typed->contenttype = retrieve_var_type(unchecked.a_variable);
                if (test == 0)
                {
                    cerr << "ERROR : Variable name \"" << unchecked.a_variable << "\" has not yet been defined\n";
                }
                else if (test == 1)
                {
                    cerr << "ERROR : Variable name \"" << unchecked.a_variable << "\" is defined but has no content assigned yet\n";
                }
            }
            return typed;
        }
        break;
        case operateur_node:
        {
            typed->nodetype = operateur_node;
            typed->an_operateur.operateur = unchecked.an_operateur.operateur;
            switch (unchecked.an_operateur.operateur)
            {
                case PROGRAM_OP:
                {
                    tree_typed* new_tree= (tree_typed*) malloc(sizeof(tree_typed));
                    (*new_tree).nodetype = variable_node;
                    (*new_tree).contenttype = (char*) nmisc.c_str();
                    (*new_tree).a_variable = unchecked.an_operateur.first->a_variable;
                    typed->an_operateur.first = new_tree;

                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case CONSTDEF_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 0);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;

                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 1);
                    return typed;
                }
                break;
                case TYPEDEFLIST_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 0);
                    typed->an_operateur.third = NULL;

                    return typed;
                }
                break;
                case TYPEDEF_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 0);
                    curr_defining.push(string(unchecked.an_operateur.first->a_variable));
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;

                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 1);
                    var_list[var_list.size()-1][string(typed->an_operateur.first->a_variable)].access_state = 2;
                    var_list[var_list.size()-1][string(typed->an_operateur.first->a_variable)].type = string(typed->an_operateur.first->a_variable);
                    type_list[string(typed->an_operateur.first->a_variable)] = inverted_types_printer(typed->an_operateur.second->contenttype);
                    curr_defining.pop();
                    return typed;
                }
                break;
                case ARRAYDEF_OP:
                {
                    typed->contenttype = (char*) narray.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;

                    if (!type_list.count(string(unchecked.an_operateur.second->a_variable)))
                    {
                        cerr << "ERROR : Array definition -> \"" << unchecked.an_operateur.second->a_variable << "\" is not a type\n";
                    }
                    else
                    {
                        array_prototype_list[curr_defining.top()] = string(unchecked.an_operateur.second->a_variable);
                    }
                    return typed;
                }
                break;
                case RECORDDEF_OP:
                {
                    map<string, access_type> field_list;
                    var_list.push_back(field_list);

                    typed->contenttype = (char*) nrecord.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 0);
                    typed->an_operateur.second = NULL;
                    typed->an_operateur.third = NULL;

                    field_list = var_list[var_list.size()-1];
                    map<string, string> new_field_list = convert_to_field(field_list);
                    record_prototype_list[curr_defining.top()] = new_field_list;
                    var_list.pop_back();
                }
                break;
                case RECORDLIST_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 0);
                    if (unchecked.an_operateur.second != NULL)
                    {
                        typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    }
                    else
                    {
                        typed->an_operateur.second = NULL;

                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case RECORDIDLIST_OP:
                {
                    tree* tmp_tree = new tree;

                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);

                    if (typed->an_operateur.second->nodetype == variable_node)
                    {
                        tmp_tree->nodetype = operateur_node;
                        tmp_tree->an_operateur.first = unchecked.an_operateur.second;
                        tmp_tree->an_operateur.second = NULL;
                        tmp_tree->an_operateur.third = NULL;
                    }
                    else
                    {
                        tmp_tree->nodetype = unchecked.an_operateur.second->nodetype;
                        tmp_tree->an_operateur.first = unchecked.an_operateur.first;
                        tmp_tree->an_operateur.second = unchecked.an_operateur.second;
                        tmp_tree->an_operateur.third = unchecked.an_operateur.third;
                    }

                    if (!type_list.count(string(unchecked.an_operateur.first->a_variable)))
                    {
                        cerr << "ERROR : Record-field definition -> \"" << unchecked.an_operateur.second->a_variable << "\" is not a type\n";
                        assign_field_type(*tmp_tree, (char*) npending.c_str());
                    }
                    else
                    {
                        assign_field_type(*tmp_tree, unchecked.an_operateur.first->a_variable);
                    }
                    typed->an_operateur.third = NULL;

                    return typed;
                }
                break;
                case IDENTIFIERLIST_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, var_arg);
                    if (unchecked.an_operateur.second != NULL)
                    {
                        typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, var_arg);
                    }
                    else
                    {
                        typed->an_operateur.second = NULL;
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case VARTYPE_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    typed->an_operateur.third = NULL;

                    string temp;
                    if (unchecked.an_operateur.first->a_variable != NULL)
                    {
                        temp = string((const char*) unchecked.an_operateur.first->a_variable);
                    }
                    else
                    {
                        temp = string(" ");
                    }
                    if (!type_list.count(temp))
                    {
                        cerr << "ERROR : Variable declaration -> \"" << unchecked.an_operateur.second->a_variable << "\" is not a type\n";
                        assign_type(*unchecked.an_operateur.second, (char*) npending.c_str());
                    }
                    else
                    {
                        assign_type(*unchecked.an_operateur.second, unchecked.an_operateur.first->a_variable);
                        if (type_list[temp] == record)
                        {
                            type_add(*unchecked.an_operateur.second, unchecked.an_operateur.first->a_variable, record);
                        }
                        else if (type_list[temp] == array)
                        {
                            type_add(*unchecked.an_operateur.second, unchecked.an_operateur.first->a_variable, array);
                        }
                    }
                    return typed;
                }
                break;
                case ARRAYACCES_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, var_arg);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    if (typed->an_operateur.first->nodetype == variable_node)
                    {
                        if (array_list.count(string(unchecked.an_operateur.first->a_variable)))
                        {
                            curr_accessing = array_list[string(unchecked.an_operateur.first->a_variable)];
                        }
                        else
                        {
                            cerr << "ERROR : Array-access -> \"" << unchecked.an_operateur.first->a_variable << "\" is not of type array\n";
                            curr_accessing = "pending";
                        }
                    }
                    else if (unchecked.an_operateur.first->an_operateur.operateur == ARRAYACCES_OP)
                    {
                        if ((array_prototype_list.count(curr_accessing)) & (curr_accessing != "pending"))
                        {
                            curr_accessing = array_prototype_list[curr_accessing];
                        }
                        else if (curr_accessing == "pending")
                        {
                            cerr << "ERROR : Array-access -> Sub-element of array-variable is of unknown type\n";
                        }
                        else
                        {
                            cerr << "ERROR : Array-access -> Sub-element of array-variable is not of type array\n";
                            curr_accessing = "pending";
                        }
                    }
                    else
                    {
                        if ((array_prototype_list.count(curr_accessing)) & (curr_accessing != "pending"))
                        {
                            curr_accessing = array_prototype_list[curr_accessing];
                        }
                        else if (curr_accessing == "pending")
                        {
                            cerr << "ERROR : Array-access -> Field of record is of unknown type\n";
                        }
                        else
                        {
                            cerr << "ERROR : Array-access -> Field of record is not of type array\n";
                            curr_accessing = "pending";
                        }
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case RECORDACCES_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, var_arg);
                    map<string, access_type> temp_map;
                    var_list.push_back(temp_map);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    var_list.pop_back();
                    if (unchecked.an_operateur.first->nodetype == variable_node)
                    {
                        if (record_list.count(string(unchecked.an_operateur.first->a_variable)))
                        {
                            string type_string = record_list[string(unchecked.an_operateur.first->a_variable)];
                            if (record_prototype_list[type_string].count(string(unchecked.an_operateur.second->a_variable)))
                            {
                                curr_accessing = record_prototype_list[type_string][string(unchecked.an_operateur.second->a_variable)];
                            }
                            else
                            {
                                cerr << "ERROR : Record-access -> \"" << unchecked.an_operateur.second->a_variable;
                                cerr << "\" is unknown as field within record-type \"" << type_string << "\"\n";
                                curr_accessing = "pending";
                            }
                        }
                        else
                        {
                            cerr << "ERROR : Record-access -> \"" << unchecked.an_operateur.first->a_variable << "\" is not a record\n";
                            curr_accessing = "pending";
                        }
                    }
                    else if (unchecked.an_operateur.first->an_operateur.operateur == ARRAYACCES_OP)
                    {
                        if (record_prototype_list.count(curr_accessing))
                        {
                            if (record_prototype_list[curr_accessing].count(string(unchecked.an_operateur.second->a_variable)))
                            {
                                curr_accessing = record_prototype_list[curr_accessing][string(unchecked.an_operateur.second->a_variable)];
                            }
                            else
                            {
                                cerr << "ERROR : Record-access -> \"" << unchecked.an_operateur.second->a_variable;
                                cerr << "\" is unknown as field within record array of type \"" << curr_accessing << "\"\n";
                                curr_accessing = "pending";
                            }
                        }
                        else
                        {
                            cerr << "ERROR : Record-access -> Array of type \"" << curr_accessing << "\" is not a record array\n";
                            curr_accessing = "pending";
                        }
                    }
                    else
                    {
                        if (record_prototype_list.count(curr_accessing))
                        {
                            if (record_prototype_list[curr_accessing].count(string(unchecked.an_operateur.second->a_variable)))
                            {
                                curr_accessing = record_prototype_list[curr_accessing][string(unchecked.an_operateur.second->a_variable)];
                            }
                            else
                            {
                                cerr << "ERROR : Record-access -> \"" << unchecked.an_operateur.second->a_variable;
                                cerr << "\" is unknown as field within record of type \"" << curr_accessing << "\"\n";
                                curr_accessing = "pending";
                            }
                        }
                        else
                        {
                            cerr << "ERROR : Record-access -> Record-field of type \"" << curr_accessing << "\" is not a record \n";
                            curr_accessing = "pending";
                        }
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case FUNCCALL_OP:
                {
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    if (func_type_list.count(string(unchecked.an_operateur.first->a_variable)))
                    {
                        typed->contenttype = (char*) func_type_list[string(unchecked.an_operateur.first->a_variable)].c_str();
                        curr_arg_list = procfunc_arg_list[string(unchecked.an_operateur.first->a_variable)];
                    }
                    else
                    {
                        typed->contenttype = (char*) npending.c_str();
                        cerr << "ERROR : Function call -> \"" << unchecked.an_operateur.first->a_variable << "\" is not a function\n";
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case ASSIGNMENT_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 1);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (curr_accessing != string(typed->an_operateur.second->contenttype))
                    {
                        cerr << "ERROR : Variable assigning -> unable to assign content of type \"" << typed->an_operateur.second->contenttype << "\" to variable of type \"" << curr_accessing << "\"\n";
                    }
                    return typed;
                }
                break;
                case PROCCALL_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    if (proc_list.count(string(unchecked.an_operateur.first->a_variable)))
                    {
                        curr_arg_list = procfunc_arg_list[string(unchecked.an_operateur.first->a_variable)];
                    }
                    else
                    {
                        cerr << "ERROR : Procedure call -> \"" << unchecked.an_operateur.first->a_variable << "\" is not a procedure\n";
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case ARGLIST_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    if (!curr_arg_list.empty())
                    {
                        if (curr_arg_list.top() != string(typed->an_operateur.first->contenttype))
                        {
                            cerr << "ERROR : Function/Procedure call -> Expected type \"" << curr_accessing << "\" instead of type \"";
                            cerr << typed->an_operateur.first->contenttype << "\" as argument " << curr_arg_list.size() << "\n";
                        }
                        curr_arg_list.pop();
                        typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                        if (unchecked.an_operateur.second->nodetype == variable_node)
                        {
                            if (!curr_arg_list.empty())
                            {
                                if (curr_arg_list.top() != string(typed->an_operateur.second->contenttype))
                                {
                                    cerr << "ERROR : Function/Procedure call -> Expected type \"" << curr_accessing << "\" instead of type \"";
                                    cerr << typed->an_operateur.second->contenttype << "\" as argument" << curr_arg_list.size() << "\n";
                                }
                                curr_arg_list.pop();
                                if (!curr_arg_list.empty())
                                {
                                    cerr << "ERROR : Function/Procedure call -> Expected " << curr_arg_list.size() << " more argument(s)\n";
                                }
                            }
                            else
                            {
                                cerr << "ERROR : Function/Procedure call -> Too many arguments given\n";
                            }
                        }
                    }
                    else
                    {
                        cerr << "ERROR : Function/Procedure call -> Too many arguments given\n";
                        typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case IFTHEN_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (type_list[string(typed->an_operateur.first->contenttype)] != boolean)
                    {
                        cerr << "ERROR : Condition testing on a non-boolean value\n";
                    }
                    return typed;
                }
                break;
                case IFTHENELSE_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = static_analysis(*unchecked.an_operateur.third, 2);
                    if (type_list[string(typed->an_operateur.first->contenttype)] != boolean)
                    {
                        cerr << "ERROR : Condition testing on a non-boolean value\n";
                    }
                    return typed;
                }
                case WHILE_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (type_list[string(typed->an_operateur.first->contenttype)] != boolean)
                    {
                        cerr << "ERROR : Condition testing on a non-boolean value\n";
                    }
                    return typed;
                }
                case FUNCDEF_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    int test = check_variable(string(unchecked.an_operateur.first->a_variable), 0);
                    if (test)
                    {
                        cerr << "ERROR : Function definition -> The name \"" << unchecked.an_operateur.first->a_variable << "\" is already in use\n";
                        curr_defining.push("def_fault");
                    }
                    else
                    {
                        var_list[var_list.size()-1][string(unchecked.an_operateur.first->a_variable)].access_state = 2;
                        var_list[var_list.size()-1][string(unchecked.an_operateur.first->a_variable)].type = "function";
                        curr_defining.push(string(unchecked.an_operateur.first->a_variable));
                    }
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.second, 1);
                    map<string, access_type> local_var_list;
                    var_list.push_back(local_var_list);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 1);
                    var_list.pop_back();
                    typed->an_operateur.third = NULL;
                    curr_defining.pop();
                    return typed;
                }
                break;
                case FUNCBLOCK_OP:
                {
                    stack<string> new_arg_list;
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 0);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 1);
                    typed->an_operateur.third = static_analysis(*unchecked.an_operateur.second, 2);

                    string temp;
                    if (typed->an_operateur.second->a_variable != NULL)
                    {
                        temp = string((const char*) typed->an_operateur.first->a_variable);
                    }
                    else
                    {
                        temp = string(" ");
                    }
                    if (!type_list.count(temp))
                    {
                        cerr << "ERROR : Function definition -> \"" << typed->an_operateur.first->a_variable << "\" is not a type\n";
                    }
                    else if (curr_defining.top() != "def_fault")
                    {
                        func_type_list[curr_defining.top()] = string(typed->an_operateur.first->a_variable);
                        stack<string> inverted_arg_list;
                        while (!new_arg_list.empty())
                        {
                            inverted_arg_list.push(new_arg_list.top());
                            new_arg_list.pop();
                        }
                        procfunc_arg_list[curr_defining.top()] = inverted_arg_list;
                    }
                    return typed;
                }
                break;
                case PROCDEF_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    int test = check_variable(unchecked.an_operateur.first->a_variable, 0);
                    if (test)
                    {
                        cerr << "ERROR : Procedure definition -> The name \"" << unchecked.an_operateur.first->a_variable << "\" is already in use\n";
                        curr_defining.push("def_fault");
                    }
                    else
                    {
                        var_list[var_list.size()-1][string(unchecked.an_operateur.first->a_variable)].access_state = 2;
                        var_list[var_list.size()-1][string(unchecked.an_operateur.first->a_variable)].type = "procedure";
                        curr_defining.push(string(unchecked.an_operateur.first->a_variable));
                    }
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.second, 1);
                    map<string, access_type> local_var_list;
                    var_list.push_back(local_var_list);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 1);
                    var_list.pop_back();
                    typed->an_operateur.third = NULL;
                    curr_defining.pop();
                    return typed;
                }
                break;
                case PROCBLOCK_OP:
                {
                    stack<string> new_arg_list;
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 1);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;

                    if (curr_defining.top() != "def_fault")
                    {
                        proc_list[curr_defining.top()] = 1;
                        stack<string> inverted_arg_list;
                        while (!new_arg_list.empty())
                        {
                            inverted_arg_list.push(new_arg_list.top());
                            new_arg_list.pop();
                        }
                        procfunc_arg_list[curr_defining.top()] = inverted_arg_list;
                    }
                    return typed;
                }
                break;
                case PARATYPE_VAR_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    if (type_list.count(string(typed->an_operateur.first->a_variable)))
                    {
                        curr_type = string(typed->an_operateur.first->a_variable);
                    }
                    else
                    {
                        cerr << "ERROR : Parameter declaration -> \"" << typed->an_operateur.first->a_variable << "\" is not a type\n";
                        curr_type = "pending";
                    }

                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    if (unchecked.an_operateur.second->nodetype == operateur_node)
                    {
                        tree_typed* new_tree= (tree_typed*) malloc(sizeof(tree_typed));
                        (*new_tree).nodetype = variable_node;
                        (*new_tree).contenttype = (char*) nmisc.c_str();
                        (*new_tree).a_variable = unchecked.an_operateur.second->a_variable;
                        typed->an_operateur.second = new_tree;
                        if (curr_defining.top() != "def_fault")
                        {
                            new_arg_list.push(curr_type);
                        }
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case PARATYPE_NOVAR_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    if (type_list.count(string(typed->an_operateur.first->a_variable)))
                    {
                        curr_type = string(typed->an_operateur.first->a_variable);
                    }
                    else
                    {
                        cerr << "ERROR : Parameter declaration -> \"" << typed->an_operateur.first->a_variable << "\" is not a type\n";
                        curr_type = "pending";
                    }

                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    if (unchecked.an_operateur.second->nodetype != operateur_node)
                    {
                        tree_typed* new_tree = (tree_typed*) malloc(sizeof(tree_typed));
                        (*new_tree).nodetype = variable_node;
                        (*new_tree).contenttype = (char*) nmisc.c_str();
                        (*new_tree).a_variable = unchecked.an_operateur.second->a_variable;
                        typed->an_operateur.second = new_tree;
                        if (curr_defining.top() != "def_fault")
                        {
                            new_arg_list.push(curr_type);
                        }
                    }
                    typed->an_operateur.third = NULL;
                    return typed;
                }
                break;
                case PARALIST_OP:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    if (curr_defining.top() != "def_fault")
                    {
                        new_arg_list.push(curr_type);
                    }
                    if (unchecked.an_operateur.second->nodetype == operateur_node)
                    {
                        typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 0);
                    }
                    else
                    {
                        tree_typed* new_tree = (tree_typed*) malloc(sizeof(tree_typed));
                        new_tree->nodetype = variable_node;
                        new_tree->contenttype = (char*) nmisc.c_str();
                        new_tree->a_variable = unchecked.an_operateur.second->a_variable;
                        typed->an_operateur.second = new_tree;
                        if (curr_defining.top() != "def_fault")
                        {
                            new_arg_list.push(curr_type);
                        }
                    }
                    return typed;
                }
                break;
                case LT_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for LowerThanStrict is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for LowerThanStrict is not an integer\n";
                    }
                    return typed;
                }
                break;
                case EQUAL_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != string(typed->an_operateur.second->contenttype))
                    {
                        cerr << "ERROR : Operator -> Arguments of Equal of different types\n";
                    }
                    return typed;
                }
                break;
                case GT_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for GreaterThanStrict is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for GreaterThanStrict is not an integer\n";
                    }
                    return typed;
                }
                case LE_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for LowerThanEqual is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for LowerThanEqual is not an integer\n";
                    }
                    return typed;
                }
                break;
                case GE_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for GreaterThanEqual is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for GreaterThanEqual is not an integer\n";
                    }
                    return typed;
                }
                break;
                case NOTEQUAL_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != string(typed->an_operateur.second->contenttype))
                    {
                        cerr << "ERROR : Operator -> Arguments of NotEqual of different types\n";
                    }
                    return typed;
                }
                break;
                case PLUS_OP:
                {
                    typed->contenttype = (char*) nint.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for Plus is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for Plus is not an integer\n";
                    }
                    return typed;
                }
                break;
                case MINUS_OP:
                {
                    typed->contenttype = (char*) nint.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for Minus is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for Minus is not an integer\n";
                    }
                    return typed;
                }
                break;
                case OR_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "boolean")
                    {
                        cerr << "ERROR : Operator -> First argument for Or is not a boolean\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "boolean")
                    {
                        cerr << "ERROR : Operator -> Second argument for Plus is not a< boolean\n";
                    }
                    return typed;
                }
                break;
                case STAR_OP:
                {
                    typed->contenttype = (char*) nint.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for Multiply is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for Multiply is not an integer\n";
                    }
                    return typed;
                }
                break;
                case DIV_OP:
                {
                    typed->contenttype = (char*) nint.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for Divide is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for Divide is not an integer\n";
                    }
                    return typed;
                }
                break;
                case MOD_OP:
                {
                    typed->contenttype = (char*) nint.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> First argument for Modulo is not an integer\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "integer")
                    {
                        cerr << "ERROR : Operator -> Second argument for Modulo is not an integer\n";
                    }
                    return typed;
                }
                break;
                case AND_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "boolean")
                    {
                        cerr << "ERROR : Operator -> First argument for And is not an boolean\n";
                    }
                    if (string(typed->an_operateur.second->contenttype) != "boolean")
                    {
                        cerr << "ERROR : Operator -> Second argument for And is not an boolean\n";
                    }
                    return typed;
                }
                break;
                case NOT_OP:
                {
                    typed->contenttype = (char*) nbool.c_str();
                    typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    typed->an_operateur.second = NULL;
                    typed->an_operateur.third = NULL;
                    if (string(typed->an_operateur.first->contenttype) != "boolean")
                    {
                        cerr << "ERROR : Operator -> Argument of Not is not a boolean\n";
                    }
                    return typed;
                }
                break;
                default:
                {
                    typed->contenttype = (char*) nmisc.c_str();
                    if (unchecked.an_operateur.first)
                    {
                        typed->an_operateur.first = static_analysis(*unchecked.an_operateur.first, 2);
                    }
                    else
                    {
                        typed->an_operateur.first = NULL;
                    }
                    if (unchecked.an_operateur.second)
                    {
                        typed->an_operateur.second = static_analysis(*unchecked.an_operateur.second, 2);
                    }
                    else
                    {
                        typed->an_operateur.second = NULL;
                    }
                    if (unchecked.an_operateur.third)
                    {
                        typed->an_operateur.third = static_analysis(*unchecked.an_operateur.third, 2);
                    }
                    else
                    {
                        typed->an_operateur.third = NULL;
                    }
                    return typed;
                }
            }
        }
        break;
    }
    return typed;
}



/* --------------------------------------- */
/* Initializing function for the variables */
/*  that are used during static analysis   */
/* --------------------------------------- */

void static_analysis_init()
{
    map<string, access_type> init_var;
    init_var["procedure"].access_state = 2;
    init_var["procedure"].type = "procedure";
    init_var["function"].access_state = 2;
    init_var["function"].type = "function";
    init_var["pending"].access_state = 2;
    init_var["pending"].type = "pending";
    init_var["integer"].access_state = 2;
    init_var["integer"].type = "integer";
    init_var["boolean"].access_state = 2;
    init_var["boolean"].type = "boolean";
    init_var["record"].access_state = 2;
    init_var["record"].type = "record";
    init_var["array"].access_state =2;
    init_var["array"].type = "array";
    var_list.push_back(init_var);

    type_list["procedure"] = procedure;
    type_list["function"] = function;
    type_list["pending"] = pending;
    type_list["integer"] = integer;
    type_list["boolean"] = boolean;
    type_list["record"] = record;
    type_list["array"] = array;

    array_list["array"] = "array";

    record_list["record"] = "record";
}



/* Added for compiling
int main()
{
    return 1;
}
*/
